/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.cadoc.cqe;

import java.util.HashMap;
import java.util.Map;

public class KnowledgeBase implements KnowledgeReceiver {

	private static int fqNamesId = 0;
	
	private Map<String, Integer> fqNames; 
	
	private Map<Integer, String> types;
	
	private Map<Integer, String> operations;
	
	private Map<Integer, String> fields;
	
	private Map<Integer, Integer> operationFacts;
	private Map<Integer, Integer> typeFacts;
	private Map<Integer, Integer> fieldFacts;
	
	private Map<Integer, Integer> fieldType;
	private Map<Integer, Integer> inv_fieldType;
	
	private Map<Integer, Integer> returnType;
	private Map<Integer, Integer> inv_returnType;
	
	private Map<Integer, Integer> declaresField;
	private Map<Integer, Integer> inv_declaresField;
	
	private Map<Integer, Integer> declaresOperation;
	private Map<Integer, Integer> inv_declaresOperation;
	
	private Map<Integer, Integer> implementsInterface;
	private Map<Integer, Integer> inv_implementsInterface;
	
	private Map<Integer, Integer> inherit;
	private Map<Integer, Integer> inv_inherit;
	
	private Map<Integer, Integer> calls;
	private Map<Integer, Integer> inv_calls;	
	
	
	public KnowledgeBase(){
		this.fqNames = new HashMap<String, Integer>();
		this.types = new HashMap<Integer, String>();
		this.operations = new HashMap<Integer, String>();
		this.fields = new HashMap<Integer, String>();
		this.operationFacts = new HashMap<Integer, Integer>();
		this.typeFacts = new HashMap<Integer, Integer>();
		this.fieldFacts = new HashMap<Integer, Integer>();
		
		this.fieldType = new HashMap<Integer, Integer>();
		this.inv_fieldType = new HashMap<Integer, Integer>();

		this.returnType = new HashMap<Integer, Integer>();
		this.inv_returnType = new HashMap<Integer, Integer>();
		
		this.declaresField = new HashMap<Integer, Integer>();
		this.inv_declaresField = new HashMap<Integer, Integer>();
		
		this.declaresOperation = new HashMap<Integer, Integer>();
		this.inv_declaresOperation = new HashMap<Integer, Integer>();
		
		this.implementsInterface = new HashMap<Integer, Integer>();
		this.inv_implementsInterface = new HashMap<Integer, Integer>();
		
		this.inherit = new HashMap<Integer, Integer>();
		this.inv_inherit = new HashMap<Integer, Integer>();
		
		this.calls = new HashMap<Integer, Integer>();
		this.inv_calls = new HashMap<Integer, Integer>();
	}
	
	/* (non-Javadoc)
	 * @see org.cadoc.cqe.KnowledgeReceiver#addType(java.lang.String)
	 */
	public void addConstruct(Construct construct, String fqn){
		if(fqn==null)return;
		
		Integer fqNameId = getFQNameId(fqn);
		switch(construct){
		case TYPE :
			types.put(fqNameId, fqn);
			break;
		case OPERATION :
			operations.put(fqNameId, fqn);
			break;
		case ELEMENT :
			fields.put(fqNameId, fqn);
			break;
		}
	}
	
	/* (non-Javadoc)
	 * @see org.cadoc.cqe.KnowledgeReceiver#addDeclaresField(java.lang.String, java.lang.String)
	 */
	public void addDeclaresField(String fqnOwner, String fqnField){
		if(fqnOwner==null || fqnField==null)return;
		
		Integer idOwner = getFQNameId(fqnOwner);
		Integer idField = getFQNameId(fqnField);
		
		this.declaresField.put(idOwner, idField);
		this.inv_declaresField.put(idField, idOwner);
	}
	
	/* (non-Javadoc)
	 * @see org.cadoc.cqe.KnowledgeReceiver#addDeclaresOperation(java.lang.String, java.lang.String)
	 */
	public void addDeclaresOperation(String fqnType, String fqnOperation){
		if(fqnType==null || fqnOperation==null)return;
		
		Integer idType = getFQNameId(fqnType);
		Integer idOperation = getFQNameId(fqnOperation);
		
		this.declaresOperation.put(idType, idOperation);
		this.inv_declaresOperation.put(idOperation, idType);
	}
	
	/* (non-Javadoc)
	 * @see org.cadoc.cqe.KnowledgeReceiver#addImplements(java.lang.String, java.lang.String)
	 */
	public void addImplements(String fqnType, String fqnInterface){
		if(fqnType==null || fqnInterface==null)return;
		
		Integer idType = getFQNameId(fqnType);
		Integer idInterface = getFQNameId(fqnInterface);
		
		this.implementsInterface.put(idType, idInterface);
		this.inv_implementsInterface.put(idInterface, idType);
	}
	
	/* (non-Javadoc)
	 * @see org.cadoc.cqe.KnowledgeReceiver#addInherit(java.lang.String, java.lang.String)
	 */
	public void addInherit(String fqnType, String fqnTypeInherit){
		if(fqnType==null || fqnTypeInherit==null)return;
		
		Integer idType = getFQNameId(fqnType);
		Integer idTypeInherit = getFQNameId(fqnTypeInherit);
		
		this.inherit.put(idType, idTypeInherit);
		this.inv_inherit.put(idTypeInherit, idType);
	}
	
	/* (non-Javadoc)
	 * @see org.cadoc.cqe.KnowledgeReceiver#addCalls(java.lang.String, java.lang.String)
	 */
	public void addCalls(String fqnCaller, String fqnCallee){
		if(fqnCallee==null || fqnCallee==null)return;
		
		Integer idCaller = getFQNameId(fqnCaller);
		Integer idCallee = getFQNameId(fqnCallee);
		
		this.calls.put(idCaller, idCallee);
		this.inv_calls.put(idCallee, idCaller);
	}	
	
	private Integer getFQNameId(String fqn){
		Integer id = this.fqNames.get(fqn);
		if(id==null){
			id = ++fqNamesId;
		}
		return id;
	}

	public void addFact(Construct construct, String fqn, int fact) {
		if(fact==0)return;
		
		Integer fqNameId = getFQNameId(fqn);
		int factVector;		
		switch (construct) {
		case TYPE:
			factVector = typeFacts.get(fqNameId);
			typeFacts.put(fqNameId, factVector|fact);
			break;
		case OPERATION:
			factVector = operationFacts.get(fqNameId);
			operationFacts.put(fqNameId, factVector|fact);
			break;
		case ELEMENT:
			factVector = fieldFacts.get(fqNameId);
			fieldFacts.put(fqNameId, factVector|fact);
			break;
		}
	}

	public void setFactVector(Construct construct, String fqn, Integer factVector) {
		Integer fqNameId = getFQNameId(fqn);
		switch (construct) {
		case TYPE:
			typeFacts.put(fqNameId, factVector);
			break;
		case OPERATION:
			operationFacts.put(fqNameId, factVector);
			break;
		case ELEMENT:
			fieldFacts.put(fqNameId, factVector);
			break;
		}
	}
	
	public void setType(Construct c, String fqn, String fqnType) {
		Integer fqNameIdField = getFQNameId(fqn);
		Integer fqNameIdType = getFQNameId(fqnType);
		
		if(c.equals(Construct.ELEMENT)){
			fieldType.put(fqNameIdField, fqNameIdType);
			inv_fieldType.put(fqNameIdType, fqNameIdField);
		}else if(c.equals(Construct.OPERATION)){
			returnType.put(fqNameIdField, fqNameIdType);
			inv_returnType.put(fqNameIdType, fqNameIdField);
			
		}
	}
	
}
